/**
 * 示例代码：实现ji.h定义的图像接口，开发者需要根据自己的实际需求对接口进行实现
 * 请先浏览本demo示例的注释，帮助快速理解和进行开发
 */

#include <cstdlib>
#include <cstring>
#include <fstream>

#include <opencv2/opencv.hpp>
#include <glog/logging.h>
#include <vector>

#include "SampleDetector.hpp"//算法自定义的头文件
#include "WKTParser.h"//ev_sdk中封装的关于几何配置（比如POLYGON，LINESTRING，POINT等）解析的库，具体位于ev_sdk/3rd/wkt_parser
#include "cJSON.h"//ev_sdk中采用cJSON库的来进行JSON数据的封装和解析，具体位于ev_sdk/3rd/cJSON
#include "ji_utils.h"//ev_sdk中封装的辅助函数
#include "ji.h"//ev_sdk主要接口定义



static const std::string algo_version = "1.0.0";//算法版本号，开发者自定义，且要求每次版本更新后，按照一致的形式修改算法版本号
static const std::string sdk_version = "4.0.0";//ev_sdk的版本号，当前是4.0.0，不用开发者自定义，除非明确了采用新版的ev_sdk

//////////////////////////////////////////////////////////////////////////////////////////////
/**
* ji_get_version:上层应用获取算法和sdk版本的接口.
* 开发者基本不用进行任何修改，除非打算重写!!!!
*/
JiErrorCode ji_get_version(char *pVersion)
{
    bool bRet = true;
    if(pVersion == nullptr)
    {
        return JISDK_RET_FAILED;
    }
    cJSON *rootObj = cJSON_CreateObject();
    cJSON_AddItemToObject(rootObj, "sdk_version", cJSON_CreateString(sdk_version.c_str()));
    cJSON_AddItemToObject(rootObj, "algo_version", cJSON_CreateString(algo_version.c_str()));

    char *jsonResultStr = cJSON_Print(rootObj);

    LOG(INFO) << "get sdk version info : " << jsonResultStr;

    if(strlen(pVersion) < strlen(jsonResultStr))
    {
        bRet = JISDK_RET_FAILED;
    }
    else
    {
        memcpy(pVersion,jsonResultStr, strlen(jsonResultStr));
    }

    if (rootObj)
        cJSON_Delete(rootObj);
    if (jsonResultStr)
        free(jsonResultStr);

    return JISDK_RET_SUCCEED;
}

JiErrorCode ji_init(int argc, char **argv)
{
    // 根据实际情况对SDK进行初始化，如授权功能
    return JISDK_RET_SUCCEED;
}

void ji_reinit()
{
    // 根据实际情况对sdk去初始化
    return;
}

////////////////////////////////////////////////////////////////////////////////////////////////
/**
* ji_create_predictor:创建具体算法实例的接口，
* 开发者需要根据自定义的算法class类进行修改，在该函数中主要进行算法class类的实例化或相关的初始化!!!!
* 可以考虑在class类的构造函数进行相关初始化，也可以调用自定义的初始化成员函数!!!
*/
void *ji_create_predictor(JiPredictorType pdtype) 
{
    auto *detector = new SampleDetector();//SampleDetector为自定义的算法class类，需要根据情况进行类名修改！！！！

    if (!detector->Init("/project/ev_sdk/model/v5s_dianti.onnx", 0.4, 0.5, "/usr/local/ev_sdk/model/coco.names")) 
    {
        delete detector;
        detector = nullptr;
        LOG(INFO) << "Predictor init failed.";
        return nullptr;
    }
    LOG(INFO) << "SamplePredictor init OK.";

    return detector;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/**
* ji_destroy_predictor:销毁算法实例的接口，
* 开发者需要根据自定义的算法class类进行修改，在该函数中主要进行算法class类的释放工作!!!!
* 可以考虑在class类的析构函数进行相关资源释放，也可以调用自定义的资源释放成员函数!!!
*/
void ji_destroy_predictor(void *predictor) 
{
    if (predictor == nullptr) return;

    auto *detector = reinterpret_cast<SampleDetector *>(predictor);//SampleDetector为自定义的算法class类，需要根据情况进行类名修改！！！！
    detector->UnInit();
    delete detector;
    detector = nullptr;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/*
* ji_calc_image:算法同步分析接口，上层应用调用最主要的入口之一，具体入参说明可参见ev_sdk/doc/EV_SDK接口规范说明.md
* 开发者需要根据自定义的算法class类进行修改，在该函数中主要是处理入参，并将相关参数传入算法class实例，并调用自定义的算法分析函数!!!!

* 该接口需要注意点是：
* 1、传入待分析的图像数据可以是单张图也可以是多张图（由nInCount指示，大多数情况算法每次分析只需要传入一张图，但也有部分情况算法分析需要同时结合多张图）
    开发者根据自定义的算法要求自行处理。
* 2、自定义的算法需要处理该接口传入的args参数，该参数通常是跟配置相关的参数信息（比如roi、识别阈值以及任何算法支持改变的控制参数）,上层应用调用时会动态传入
    args参数，算法分析时需要动态适应。
* 3、输出的结果图（通常会存在需要将算法分析的一些结果信息显示到与原图相同大小的结果图上）放在pOutFrames中；同样地，输入几张图就对应几张输出图，数量由nOutCount指示。
* 4、算法输出的所有结构化信息（具体信息内容根据业务及算法决定，相关的一些基本规则可参见ev_sdk/doc/极市算法SDK输出协议.md）均以JSON规范封装在JiEvent数据结构中。
*/
JiErrorCode ji_calc_image(void* predictor, const JiImageInfo* pInFrames, const unsigned int nInCount, const char* args, 
						JiImageInfo **pOutFrames, unsigned int & nOutCount, JiEvent &event)
{
    if (predictor == nullptr || pInFrames == nullptr || nInCount <= 0) 
    {
        return JISDK_RET_INVALIDPARAMS;
    }

    auto *detector = reinterpret_cast<SampleDetector *>(predictor);//SampleDetector为自定义的算法class类，需要根据情况进行类名修改！！！！
    bool processRet  = false;
    ///////////////////////////////////////////////////////////////////////////////
    // 例： 分析单张图片，输出单张图片，算法内部使用cv::Mat格式数据
    if(nInCount == 1)
    {
        cv::Mat inMat(pInFrames[0].nHeight, pInFrames[0].nWidth, CV_8UC3, pInFrames[0].pData);//获取到需要进行分析的图像数据
        processRet = detector->Process(inMat, args, event);//算法进行分析
    }
    else
    {
        /* 多图自行实现,如：
        for(unsigned int i = 0 ; i < nInCount; i++)
        {
            ...
        }
        */
    }
    
    if (processRet)
    {
        if (event.code != JISDK_CODE_FAILED)
        {
            detector->GetOutFrame(pOutFrames,nOutCount);//算法处理输出结果图
            // if(event.json)
            // delete event.json;
            return JISDK_RET_SUCCEED;
        }
        else
        {
            return JISDK_RET_FAILED;
        }
    }
    return JISDK_RET_FAILED;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/*
* ji_update_config:算法配置参数更新接口，上层应用显式调用算法配置更新
* 建议开发者在自定义的算法class类中实现一个专门用于处理和更新配置参数的成员函数!!!!
*/
JiErrorCode ji_update_config(void *predictor, const char *args)
{
    if(args == nullptr)
    {
        LOG(ERROR) << "config string is null ";
        return JISDK_RET_FAILED;
    }
    LOG(INFO) << "update : " << args;
    auto *detector = reinterpret_cast<SampleDetector *>(predictor);//SampleDetector为自定义的算法class类，需要根据情况进行类名修改！！！！

    if(!detector->UpdateConfig(args))//调用自定义算法class类的配置update函数
        return JISDK_RET_FAILED;
    
    return JISDK_RET_SUCCEED;
}

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////
/*
* ji_set_callback:算法异步分析相关的callback设置接口
* 在本demo中未实现，可具体根据项目和算法要求进行相关实现!!!!
*/
JiErrorCode ji_set_callback(void *predictor, JiCallBack callback)
{
    return JISDK_RET_UNUSED;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/*
* ji_calc_image_asyn:算法异步分析接口
* 在本demo中未实现，可具体根据项目和算法要求进行相关实现!!!!
*/
JiErrorCode ji_calc_image_asyn(void* predictor, const JiImageInfo* pInFrames, const unsigned int nInCount, const char* args, void *userData)
{
    
    return JISDK_RET_UNUSED;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* ji_face_*:与人脸识别相关的接口
* 在本demo中未实现，可具体根据项目和算法要求进行相关实现!!!!
*/
JiErrorCode ji_face_insert(void *predictor, const char *imgPath, const int libId)
{
    return JISDK_RET_UNUSED;
}

JiErrorCode ji_face_delete(void *predictor, const char *faceId, const int libId)
{
    return JISDK_RET_UNUSED;
}

JiErrorCode ji_face_update(void *predictor, const int libId, const char *imgPath)
{
    return JISDK_RET_UNUSED;
}

JiErrorCode ji_face_init(void *predictor, const char *whitePath, const char *blackPath)
{
    return JISDK_RET_UNUSED;
}

JiErrorCode ji_face_exportDB(void *predictor, const char *outPath, const int libId)
{
    return JISDK_RET_UNUSED;
}
